﻿(*                  FnSharp              

   Base class library extensions and frameworks aimed 
   specifically at improving F# developers lives!

   Written by: Huw Simpson
   License: Microsoft Public License (Ms-PL) 
   Available at: http://fnsharp.codeplex.com

*)
namespace FnSharp.Bcl.Core

    open System
    open System.Collections.Generic

    [<RequireQualifiedAccess>]
    [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
    module Seq =
        /// Group the items in a list by a supplied function which returns the desired grouping action.
        [<CompiledName("GroupBy2")>]
        let groupBy2 f (source:'t seq) = seq {
            let group = new List<'t> ()
            let drain () = group.Count > 0 |> Option.ofBoolF2 (group.ToArray >> Array.toSeq) (ignore >> group.Clear)

            for e in source do
                match f e (group.ToArray ()) with
                | YieldGroup itemAction ->
                    match drain (), itemAction with
                    | None,     GroupItem    -> group.Add e
                    | Some g,   GroupItem    -> group.Add e; yield g
                    | None,     AppendItem   -> yield seq [e]
                    | Some g,   AppendItem   -> yield seq [e] |> Seq.append g
                    | None,     DiscardItem  -> ()
                    | Some g,   DiscardItem  -> yield g
                    | None,     SeperateItem -> yield seq [e]
                    | Some g,   SeperateItem -> yield g; yield seq [e]
                | ContinueGroup itemAction ->
                    match itemAction with
                    | WithItem -> group.Add e
                    | WithoutItem -> () 

            match drain () with 
            | None -> ()
            | Some g -> yield g }          
            
        /// Group the items in a list by a supplied function which returns the desired grouping action.
        [<CompiledName("GroupBy")>]
        let inline groupBy f source =
            let fx a b = f a
            source |> groupBy2 fx

        /// Return the nth item of a sequence as an option, if it exists, or none.
        [<CompiledName("NthOrNone")>]
        let inline nthOrNone index (source:'t seq) =
            use en = source.GetEnumerator ()
            try
                if not (en.MoveNext ()) then None
                elif index = 0 then Some (en.Current)
                else
                    let mutable i = 0
                    while i < index && en.MoveNext () do i <- i + 1
                    i = index |> Option.ofBool (en.Current)
            with _ -> None

        /// Return the nth item of a sequence, if it exists, or the supplied default value.
        [<CompiledName("NthOrDefaultTo")>]
        let inline nthOrDefaultTo index defaultValue (source:'t seq) =
            source |> nthOrNone index |> Option.mapOrDefaultTo id defaultValue

        /// Return the nth item of a sequence, if it exists, or the generic default.
        [<CompiledName("NthOrDefault")>]
        let inline nthOrDefault index (source:'t seq) =
            source |> nthOrNone index |> Option.mapOrDefault id